
""" GUI Frames
"""

"""
    Copyright 2008 Graham King <graham@gkgk.org>
    
    This file is part of Keithnote.

    Keithnote is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Keithnote is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Keithnote.  If not, see <http://www.gnu.org/licenses/>.
"""

# wx Frame objects have loads of methods
# pylint: disable-msg=R0904

import threading
import time

import wx

import presentation

DEFAULT_SLIDES = 10

class ButtonBar(wx.Panel):
    "Runs along the top of HomeFrame with the action buttons"
    
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        text = wx.StaticText(self, wx.ID_ANY, "Presentations", style=wx.ALIGN_CENTER)
        text.SetFont( wx.Font(18, wx.DECORATIVE, wx.NORMAL, wx.NORMAL) )
        
        new_button = wx.Button(self, wx.ID_NEW)
        self.Bind(wx.EVT_BUTTON, self.onNew, new_button)
        
        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.sizer.Add(text, 1, wx.EXPAND)
        self.sizer.Add(new_button, 0, wx.EXPAND)
        self.SetSizer(self.sizer)
    
    def onNew(self, event):        # Not using 'event'  pylint: disable-msg=W0613
        "'New' button clicked"
        
        frame = EditFrame(self.GetTopLevelParent())
        frame.Show()


class Presentation(wx.Panel):
    "A presentation"
    
    def __init__(self, parent, pres):
        wx.Panel.__init__(self, parent, wx.ID_ANY, style=wx.BORDER_SUNKEN)
        
        self.pres = pres
        
        self.SetBackgroundColour('White')
        
        title = wx.StaticText(self, wx.ID_ANY, pres.title)
        bold_font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD)
        title.SetFont(bold_font)
        
        author = wx.StaticText(self, wx.ID_ANY, pres.author +" - "+ pres.affiliation)
        tags = wx.StaticText(self, wx.ID_ANY, pres.tags)
        
        self.inner_sizer = wx.BoxSizer(wx.VERTICAL)
        self.inner_sizer.Add(title, 0, wx.EXPAND)
        self.inner_sizer.Add(author, 0, wx.EXPAND)
        self.inner_sizer.Add(tags, 0, wx.EXPAND)
        
        self.play_button = wx.Button(self, wx.ID_ANY, "Play")
        self.delete_button = wx.Button(self, wx.ID_DELETE)
                
        self.outer_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.outer_sizer.Add((20, -1), 0, wx.EXPAND)
        self.outer_sizer.Add(self.inner_sizer, 1, wx.EXPAND)
        self.outer_sizer.Add((20, -1), 0, wx.EXPAND)
        self.outer_sizer.Add(self.play_button, 0, wx.EXPAND)
        self.outer_sizer.Add((20, -1), 0, wx.EXPAND)
        self.outer_sizer.Add(self.delete_button, 0, wx.EXPAND)
        self.SetSizer(self.outer_sizer)

        self.Bind(wx.EVT_BUTTON, self.onDelete, self.delete_button)
        self.Bind(wx.EVT_BUTTON, self.onPlay, self.play_button)
        
    def onDelete(self, event):        # Not using 'event'  pylint: disable-msg=W0613
        'Delete button clicked'
        
        dlg = wx.MessageDialog(None,
                               "Delete presentation '"+ self.pres.title +"'?", 
                                "Confirm delete",
                                style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
        confirm = dlg.ShowModal()
        
        if confirm == wx.ID_YES:
            self.pres.delete()
            self.GetTopLevelParent().reload_presentations()
        
    def onPlay(self, event):        # Not using 'event'  pylint: disable-msg=W0613
        'Play button clicked'
        
        SlideshowFrame(self.pres.ident)


class HomeFrame(wx.Frame):
    "The main frame which lists all the presentations"
    
    def __init__(self):
        wx.Frame.__init__(
                                        self, 
                                        parent=None, 
                                        id=wx.ID_ANY, 
                                        title="Keithnote", 
                                        size=(600,300)
                                        )

        self.panel = wx.Panel(self, wx.ID_ANY)
        
        self.button_bar = None
        self.sizer = None
        self._create()

    def _create(self):
        'Creates the display by loading all the presentations from disc'
        
        self.button_bar = ButtonBar(self.panel)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.button_bar, 0, wx.EXPAND)        
        
        presentations = presentation.get_presentations()
        for pres in presentations:
            pres_gui = Presentation(self.panel, pres)
            self.sizer.Add(pres_gui, 0, wx.EXPAND)        

        self.panel.SetSizerAndFit(self.sizer)
        self.SetClientSize( self.panel.GetSize() )  # Resize the frame to fit all the presentations
        self.Refresh(True)
        self.Centre()
                
    def reload_presentations(self):
        'Clear the current display and load all the presentations'
        
        self.sizer.DeleteWindows()
        self._create()


class EditFrame(wx.Frame):
    "Edit a presentation"
    
    def __init__(self, home_frame):
        wx.Frame.__init__(
                          self,
                          parent=home_frame,
                          id=wx.ID_ANY,
                          title="Edit presentation",
                          style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_TOOL_WINDOW,
                          size=(400,260)
                          )
        
        self.home_frame = home_frame
        
        self.panel = wx.Panel(self, wx.ID_ANY)
        
        header = self._create_header()
        
        self.title = None
        self.author = None
        self.affiliation = None
        self.tags = None
        self.slides = None        
        self.relevance_radio = None
        self.interestingness_radio = None
        form = self._create_form()
        
        button = self._create_button()
        
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(header, 0, wx.EXPAND)
        self.sizer.Add(form, 1, wx.EXPAND)
        self.sizer.Add(button, 0, wx.ALIGN_BOTTOM | wx.ALIGN_CENTER)
        self.panel.SetSizer(self.sizer)

        self.Centre()

        self.progress_dialog = None

    def _create_header(self):
        'Create and return the header area'
        
        header = wx.StaticText(self.panel, wx.ID_ANY, "New Presentation")        
        header.SetFont( wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD) )

        header_sizer = wx.BoxSizer(wx.VERTICAL)
        header_sizer.Add(header, 0, wx.ALL, 5)
        header_sizer.Add( wx.StaticLine(self.panel), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)

        return header_sizer
        
    def _create_form(self):
        'Create and return the form area'
        
        title_label = wx.StaticText(self.panel, wx.ID_ANY, "Title:")
        self.title = wx.TextCtrl(self.panel, wx.ID_ANY)
        self.title.SetFocus()
        
        author_label = wx.StaticText(self.panel, wx.ID_ANY, "Author:")
        self.author = wx.TextCtrl(self.panel, wx.ID_ANY)
        
        affiliation_label = wx.StaticText(self.panel, wx.ID_ANY, "Affiliation:")
        self.affiliation = wx.TextCtrl(self.panel, wx.ID_ANY)
        
        tags_label = wx.StaticText(self.panel, wx.ID_ANY, "Extra tags:")
        self.tags = wx.TextCtrl(self.panel, wx.ID_ANY)
 
        slides_label = wx.StaticText(self.panel, wx.ID_ANY, "Slides:")
        self.slides = wx.SpinCtrl(self.panel, wx.ID_ANY, min=1, max=25, initial=DEFAULT_SLIDES)
 
        sort_label = wx.StaticText(self.panel, wx.ID_ANY, "Select images by:")
        self.relevance_radio = wx.RadioButton(self.panel, wx.ID_ANY, "Relevance", style=wx.RB_GROUP)
        self.interestingness_radio = wx.RadioButton(self.panel, wx.ID_ANY, "Interestingness")
        sort_sizer = wx.BoxSizer(wx.HORIZONTAL)
        sort_sizer.Add(self.relevance_radio, 0, wx.EXPAND)
        sort_sizer.Add(self.interestingness_radio, 0, wx.EXPAND)

        form_sizer = wx.FlexGridSizer(cols=2)
        form_sizer.AddGrowableCol(1)

        form_sizer.Add(title_label, 0, wx.ALIGN_RIGHT)
        form_sizer.Add(self.title, 0, wx.EXPAND)
        
        form_sizer.Add(author_label, 0, wx.ALIGN_RIGHT)
        form_sizer.Add(self.author, 0, wx.EXPAND)
        
        form_sizer.Add(affiliation_label, 0, wx.ALIGN_RIGHT)
        form_sizer.Add(self.affiliation, 0, wx.EXPAND)
        
        form_sizer.Add(tags_label, 0, wx.ALIGN_RIGHT)
        form_sizer.Add(self.tags, 0, wx.EXPAND)
        
        form_sizer.Add(slides_label, 0, wx.ALIGN_RIGHT)
        form_sizer.Add(self.slides, 0)
        
        form_sizer.Add(sort_label, 0, wx.ALIGN_RIGHT)
        form_sizer.Add(sort_sizer, 0, wx.EXPAND)

        return form_sizer
        
    def _create_button(self):
        'Create and return the Save button area'
        
        save_button = wx.Button(self.panel, wx.ID_SAVE)
        save_button.SetDefault()
        
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add( (10, 10), 1 )
        button_sizer.Add(save_button, 0, wx.ALIGN_CENTER)
        button_sizer.Add( (10, 10), 1 )
        
        self.Bind(wx.EVT_BUTTON, self.onSave, save_button)
        
        return button_sizer

    def onSave(self, event):            # Not using 'event'  pylint: disable-msg=W0613
        'Called when Save is clicked'
        
        title = self.title.GetValue()
        author = self.author.GetValue()
        affiliation = self.affiliation.GetValue()
        tags = self.tags.GetValue()
        slides = self.slides.GetValue()
        
        if self.relevance_radio.GetValue():
            sort = "relevance"
        else:
            sort = "interestingness-desc"

        self.progress_dialog = wx.ProgressDialog(
                              "  Fetching slides  ", 
                              "Starting...", 
                              maximum = int(slides), 
                              parent = self,
                              style = wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL | wx.PD_SMOOTH
                              )
        
        thrd = CreatePresentationThread(
                                        title, 
                                        author, 
                                        affiliation, 
                                        tags,
                                        slides, 
                                        sort,
                                        self.progress_dialog, 
                                        self.onSaveComplete)
        thrd.start()
        
    def onSaveComplete(self):
        'Called by CreatePresentationThread when the presentation is created'
        self.home_frame.reload_presentations()
        self.Destroy()


class CreatePresentationThread(threading.Thread):
    "Thread wrapper around the creation of a presentation, which involves downloading images"
    
    def __init__(
                 self, 
                 title, 
                 author, 
                 affiliation, 
                 tags,
                 slides,
                 sort,
                 progress_dialog,
                 callback
                 ):
        threading.Thread.__init__(self)
        
        self.title = title
        self.author = author
        self.affiliation = affiliation
        self.tags = tags
        self.slides = slides
        self.sort = sort
        self.progress_dialog = progress_dialog
        self.callback = callback

    def run(self):
        'Main method of a thread'

        presentation.create(
                            self.title, 
                            self.author, 
                            self.affiliation, 
                            self.tags, 
                            self.slides,
                            self.sort,
                            self.progress_dialog
                            )

        time.sleep(0.5)    # Wait for the ProgressDialog to catch up on its events
        wx.CallAfter( self.progress_dialog.Close )

        time.sleep(0.5)    # Wait for the progress dialog to close
        wx.CallAfter( self.callback )


class SlideshowFrame(wx.Frame):
    'The actual slideshow'
    
    def __init__(self, ident):
        wx.Frame.__init__(
                          self, 
                          parent=None,
                          id=wx.ID_ANY,
                          title="Presentation",
                          style=wx.DEFAULT_FRAME_STYLE | wx.WANTS_CHARS
                          )

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panel.SetBackgroundColour('Black')
        self.panel.SetForegroundColour('White')
        #self.panel.Bind(wx.EVT_CHAR, self.onKeypress)
        self.panel.Bind(wx.EVT_KEY_UP, self.onKeyup)    # For some reason wx.EVT_CHAR doesn't work on Windows
        
        self.pres = presentation.load_presentation(ident)
        self.slides = self.pres.slides()
        self.next_slide = 0
        
        self.sizer = None
        self.title = None
        self.author = None
        self.affiliation = None
        self.bmp = None

        window_size = self.panel.GetClientSize()
        self.count = wx.StaticText(self.panel, wx.ID_ANY, "", pos=(20, window_size.height - 50))
        self.count.SetFont( wx.Font(18, wx.SWISS, wx.NORMAL, wx.NORMAL) )
        
        self._show_intro_screen()

        self.panel.SetFocus()
        self.ShowFullScreen(True)

    def _show_intro_screen(self):
        'Opening slide of the presentation with the title, author, etc'
        
        self.title = wx.StaticText(self.panel, wx.ID_ANY, self.pres.title )
        self.title.SetFont( wx.Font(76, wx.SWISS, wx.NORMAL, wx.BOLD) )

        self.author = wx.StaticText(self.panel, wx.ID_ANY, self.pres.author)
        self.author.SetFont( wx.Font(40, wx.SWISS, wx.NORMAL, wx.BOLD) )

        self.affiliation = wx.StaticText(self.panel, wx.ID_ANY, self.pres.affiliation)
        self.affiliation.SetFont( wx.Font(36, wx.SWISS, wx.NORMAL, wx.NORMAL) )

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add((-1, 300), 0)
        self.sizer.Add(self.title, 0, wx.EXPAND, 20)
        self.sizer.Add((-1, 100), 0)
        self.sizer.Add(self.author, 0, wx.EXPAND, 20)
        self.sizer.Add(self.affiliation, 0, wx.EXPAND, 20)
        self.panel.SetSizer(self.sizer)
        
    def _show_credits(self):
        'Display the last screen, with the credits'
        
        self._wipe_screen()
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add( (-1, 150), 0, wx.EXPAND )
        
        slide_num = 1
        for (title, author) in self.pres.credits:
            
            msg = str(slide_num) +'. '+ title +' by '+ author
            text = wx.StaticText(self.panel, wx.ID_ANY, msg)
            text.SetFont( wx.Font(40, wx.SWISS, wx.NORMAL, wx.NORMAL) )
            self.sizer.Add(text, 0, wx.ALIGN_CENTER)
            slide_num += 1

        self.panel.SetSizer(self.sizer)
        self.panel.Layout()
        
    def _wipe_screen(self):
        'Remove all visible elements from the screen, in preparation for showing the next slide'

        if self.bmp:
            self.bmp.Destroy()
            self.bmp = None

        if self.sizer:
            self.sizer.DeleteWindows()
        
        if self.count:
            self.count.Destroy()
            self.count = None

    def _next_slide(self):
        'Show the next slide'

        self._wipe_screen()
        slide = self.slides[self.next_slide]
    
        img = wx.Image(slide, wx.BITMAP_TYPE_ANY)
        self.bmp = wx.StaticBitmap(
                                    self.panel, 
                                    wx.ID_ANY, 
                                    wx.BitmapFromImage(img), 
                                    size=(img.GetWidth(), img.GetHeight()) 
                                    )
        self.bmp.Center()

        self._show_slide_count(self.next_slide)
        
        self.next_slide += 1
        
    def _show_slide_count(self, slide_num):
        'Display which number slide this is and how many there are total'
        
        msg = str(slide_num + 1) +" / "+ str(len(self.slides))
        window_size = self.panel.GetClientSize()
        self.count = wx.StaticText(self.panel, wx.ID_ANY, msg, pos=(20, window_size.height - 50))
        self.count.SetFont( wx.Font(18, wx.SWISS, wx.NORMAL, wx.NORMAL) )
        
    def onKeyup(self, event):
        'Called when a key is pressed'

        if event.GetKeyCode() == wx.WXK_ESCAPE:
            self.Close()
        else:
            if self.next_slide < len(self.slides):
                self._next_slide()
            else:
                self._show_credits()
        
        event.Skip()
